<?php
namespace Org\Su;

/**
 * 参数收集器
 * 用于收集表单参数等,带有验证功能
 *
 * @author Alen
 * @since 1.0 2014-6-17 by sutroon
 */
class Params {

    /**
     * 模型实例
     * @var \Think\Model
     */
    protected $M;

    /**
     * 错误集
     * @var array
     */
    protected $error = null;

    /**
     * 参数集
     * 收集符合TP模型操作规范的参数
     * @var array
     */
    protected $params = null;

    /**
     * 静态参数集
     * 构建静态sql查询语句
     * @var array
     */
    protected $literal_params = null;

    /**
     * 原始参数集
     * 用于页面查询条件或分页查询条件的收集
     * @var array
     */
    protected $origin_params = null;

    /**
     * 存储过程Out参数
     * 多个参数之间以逗号隔开
     * @var type
     * @example 如:@id,@result_code
     * @since 1.0 2014-6-30 by sutroon
     */
    protected $outParams = '';

    public function __construct() {
        $this->M = new \Think\Model();
    }

    /**
     * 添加一个接收自表单的参数
     * 该参数用于表单验证,验证通过后添加到参数集合中
     *
     * @param string $name 字段名称
     * @param mixed $rule 验证规则,支持require,email,url,currency,number,zip,integer,double,english等内置验证规则
     * @param string $msg 错误消息
     * @param boolean $optional 是否可选参数,如果true则值是空值时会忽略判断
     * @param mixed $value 值,默认$value=I($name);
     * @since 1.0 2014-6-18 by sutroon
     */
    public function addParam($name, $rule, $msg, $optional = false, $value = null) {
        if ($value == null) {
            $value = I($name);
        }
        if ($optional && !$value) {
            return;
        }
        if ($this->M->check($value, $rule)) {
            $this->params[$name] = $value;
        } else {
            $this->error[] = $msg;
        }
    }

    /**
     * 添加可选参数
     *
     * @param string $name 字段名称
     * @param mixed $rule 验证规则,支持require,email,url,currency,number,zip,integer,double,english等内置验证规则
     * @param string $msg 错误消息
     * @param mixed $value 值,默认$value=I($name);
     * @return void
     * @since 1.0 2014-8-15 by sutroon
     * @example addOptional('name','require','用户名不能为空');
     */
    public function addOptional($name, $rule = '', $msg = '', $value = null) {
        if ($value == null) {
            $value = I($name);
        }
        if (strlen($value) < 1) {
            return;
        }
        if ($rule && !$this->M->check($value, $rule)) {
            $this->error[] = $msg;
            return;
        }
        $this->params[$name] = $value;
    }

    /**
     * 添加静态参数
     * 用于收集查询字段等
     *
     * @param string $name 字段名称
     * @param mixed $rule 验证规则,支持require,email,url,currency,number,zip,integer,double,english等内置验证规则
     * @param string $fmt 格式字符串
     * @param mixed $value 值,默认$value=I($name);
     * @return void
     * @since 1.0 2014-9-19 by sutroon
     * @example
     *      addLiteralParam('','',"recordfile <> ''");
     *      addLiteralParam('date','require',"date_format(start,'%%Y-%%m-%%d')='%s'");
     *      addLiteralParam('billsec', 'number', 'billsec > %d');
     */
    public function addLiteralParam($name, $rule, $fmt, $value = null) {
        if ($value == null) {
            $value = $name ? I($name) : $fmt;
        }
        if ($name) {
            $this->origin_params[$name] = $value;
        }
        if (strlen($value) < 1) {
            return;
        }
        if ($rule && !$this->M->check($value, $rule)) {
            return;
        }
        if ($name) {
            $this->literal_params[$name] = sprintf($fmt, $value);
        } else {
            $this->literal_params[] = $fmt;
        }
    }

    /**
     * 设置表达式
     * @param string $name 字段名称
     * @param mixed $exp 如：[NOT] BETWEEN, [NOT] IN, [%]LIKE[%], array(array('gt',1),array('lt',10))
     * @return void
     * @since 1.0 2014-8-15 by sutroon
     *      1.1 2014-9-5 by sutroon $exp支持数组,可直接传入表达式,用于支持$map['id'] = array(array('gt',1),array('lt',10))的操作
     */
    public function setExpression($name, $exp) {
        if (!array_key_exists($name, $this->params)) {
            return;
        }
        if (is_array($exp)) {
            $this->params[$name] = $exp; // 如$map['id'] = array(array('gt',1),array('lt',10)) ;
            return;
        }
        switch ($exp) {
            case '=':
                $this->params[$name] = array('EQ', $this->params[$name]);
                break;
            case '>':
                $this->params[$name] = array('GT', $this->params[$name]);
                break;
            case '>=':
                $this->params[$name] = array('EGT', $this->params[$name]);
                break;
            case '<':
                $this->params[$name] = array('LT', $this->params[$name]);
                break;
            case '<=':
                $this->params[$name] = array('ELT', $this->params[$name]);
                break;
            case '<>':
                $this->params[$name] = array('NEQ', $this->params[$name]);
                break;
            default:
                if (strpos($exp, 'like') !== false) {
                    $this->params[$name] = array('LIKE', str_replace('like', $this->params[$name], $exp));
                } else {
                    $this->params[$name] = array($exp, $this->params[$name]);
                }
                break;
        }
    }

    /**
     * 批量获取参数
     * @param array $fields exfield字段定义数组
     * @return false|array 如果有值则返回false,否则返回数组
     * @since 1.0 2014-12-12 by sutroon
     * @example 
     * $list = array(
      array('field' => 'group', 'type' => 'varchar', 'length' => '16', 'null' => 'NO', 'default' > '', 'comment' => '客户群组(A,B,C,D,E)', 'label' => '客户群组', 'inputType' => 'select', 'optionalValue' => 'A,B,C,D,E', 'isRequire' => 1),
      array('field' => 'source', 'type' => 'varchar', 'length' => '16', 'null' => 'NO', 'default' > '电话', 'comment' => '客户来源(电话,QQ,微信,广告,网站,其他)', 'label' => '客户来源', 'inputType' => 'select', 'optionalValue' => '电话,QQ,微信,广告,网站,其他', 'isRequire' => 1),
     */
    public function patchParams($fields) {
        $data = false;
        // int,smallint,decimal,varchar,date,datetime,text
        foreach ($fields as $row) {
            $name = trim($row['field']);
            if ($name == '' || $name == '?') {
                continue;
            }
            $type = $row['type'];
            switch ($type) {
                case 'int':
                case 'smallint':
                    $rule = 'number';
                    break;
                case 'date':
                    $rule = 'date';
                    break;
                case 'datetime':
                    $rule = 'datetime';
                    break;
                case 'decimal':
                    $rule = 'double';
                    break;
                default:
                    $rule = 'require';
                    break;
            }
            $value = I($name);
            if ($value && is_array($value)) {
                $value = implode(',', $value);
            }
            if ($row['isRequire']) {
                if (!$this->M->check($value, $rule)) {
                    $this->error[] = $row['label'] . '无效';
                    continue;
                }
            } else {
                if ($value && !$this->M->check($value, $rule)) {
                    $this->error[] = $row['label'] . '错误';
                    continue;
                }
            }
            $data[$name] = $value;
        }
        return $data;
    }

    /**
     * 更新一个参数的值
     *
     * @param string $name 字段名称
     * @param mixed $value 值
     * @since 1.0 2014-6-18 by sutroon
     */
    public function updateParam($name, $value) {
        if (!array_key_exists($name, $this->params)) {
            return;
        }
        $this->params[$name] = $value;
    }

    /**
     * 移除一个参数
     * @param string|array $name 字段名称
     * @since 1.0 2014-6-21 by sutroon
     */
    public function removeParam($name) {
        if (is_array($name)) {
            foreach ($name as $i) {
                unset($this->params[$i]);
            }
        } else {
            unset($this->params[$name]);
        }
    }

    /**
     * 获取一个字段的值
     *
     * @param string $name 字段名称
     * @return mixed 参数的值
     * @since 1.0 2014-6-18 by sutroon
     */
    public function getParam($name) {
        return $this->params[$name];
    }

    /**
     * 检查两个值是否一致
     * 用于密码和确认密码的比较等
     *
     * @param mixed $value1
     * @param mixed $value2
     * @param string $msg
     * @since 1.0 2014-6-18 by sutroon
     */
    public function confirm_param($value1, $value2, $msg) {
        if ($value1 != $value2)
            $this->error[] = $msg;
    }

    /**
     * 检查验证码是否正确
     *
     * @param string $value
     * @param string $msg
     * @example check_captcha(I('captcha'));
     * @since 1.0 2014-6-18 by sutroon
     */
    public function check_captcha($value, $msg = '验证码错误!') {
        if (!(session('captcha') && session('captcha') == md5($value))) {
            $this->error[] = $msg;
        }
        session('captcha', null);
    }

    /**
     * 添加存储过程的Out参数
     * 变量名称需加前缀符号@
     * @param array $arrParams
     * @since 1.0 2014-6-30 by sutroon
     * @example addOutParams('@oint_code');
     */
    public function addOutParams($arrParams) {
        foreach ($arrParams as &$v) {
            if ($v[0] !== '@') {
                $v = '@' . $v;
            }
        }
        $this->outParams .= implode(',', $arrParams);
    }

    /**
     * 添加一个数据
     *
     * @param string $name 字段名称
     * @param mixed $value 值,默认$value=I($name);
     * @since 1.0 2014-6-18 by sutroon
     */
    public function addData($name, $value = null) {
        if (is_null($value)) {
            $value = I($name);
        }
        $this->params[$name] = $value;
    }

    /**
     * 添加一条错误消息
     * @param string $msg
     * @since 1.0 2014-6-23 by sutroon
     */
    public function addError($msg) {
        $this->error[] = $msg;
    }

    /**
     * 检查是否有错误消息
     *
     * @return boolean
     */
    public function hasError() {
        return $this->error ? true : false;
    }

    /**
     * 清空错误消息
     */
    public function clearError() {
        $this->error = null;
    }

    /**
     * 获取错误消息的字符串格式
     *
     * @param type $splitor 错误消息断行符号,默认是'&lt;br /&gt;'
     * @return string
     */
    public function getError($splitor = '<br />') {
        if (!$this->error)
            return '';
        return implode($splitor, $this->error);
    }

    /**
     * 获取错误消息对象
     * @return array
     * @since 1.0 2014-8-12 by sutroon
     */
    public function getErrors() {
        return $this->error;
    }

    /**
     * 获取静态参数
     * @return string
     * @since 1.0 2014-9-19 by sutroon
     */
    public function getLiteralParams() {
        return $this->literal_params ? implode(' and ', $this->literal_params) : '';
    }

    /**
     * 清空静态参数
     * @since 1.0 2014-9-19 by sutroon
     */
    public function clearLiteralParams() {
        unset($this->literal_params);
    }

    /**
     * 获取参数集
     * @param array $filter 过滤的字段名称,默认null
     * @param array $keep 保留的字段名称,默认null
     * @return array
     * @since 2014-10-14 1.1 新增$filter参数; 2014-10-17 1.2 新增$keep参数
     */
    public function getParams($filter = null, $keep = null) {
        if (is_array($filter) || is_array($keep)) {
            $hasKeep = is_array($keep);
            $hasFilter = is_array($filter);
            foreach ($this->params as $k => $v) {
                if ($hasKeep && in_array($k, $keep)) {
                    $arrOut[$k] = $v;
                    continue;
                }
                if ($hasFilter && !in_array($k, $filter)) {
                    $arrOut[$k] = $v;
                    continue;
                }
            }
            return $arrOut;
        }
        return $this->params;
    }

    /**
     * 获取原始参数集
     * @return array
     * @since 1.0 2014-9-19 by sutroon
     */
    public function getOrginalParams() {
        return $this->origin_params;
    }

    /**
     * 清空参数
     * @since 1.0 2014-7-14 by sutroon
     */
    public function clearParams() {
        unset($this->params);
    }

    /**
     * 从添加的参数中获取Insert语句命令
     * @param string $table 数据表名称
     * @param null $filter 过滤的字段集
     * @param null $keep 保留的字段集
     * @return string 返回 insert into ...语句
     * @since 1.0 2014-6-25 by sutroon created; 2014-10-17 1.1 by sutroon 新增$filter,$keep参数
     */
    public function get_insert_sql($table, $filter = null, $keep = null) {
        $data = $this->getParams($filter, $keep);
        if (!$data) {
            return '';
        }
        $strfields = '';
        $strvalues = '';
        foreach ($data as $k => $v) {
            $strfields .= "`$k`,";
            $strvalues .= "'$v',";
        }
        return sprintf('insert into %s (%s) values (%s);', $table, rtrim($strfields, ','), rtrim($strvalues, ','));
    }

    /**
     * 从添加的参数中获取Update语句命令
     * @param string $table 数据表名称
     * @param string $search 条件语句,如果全表更新需加 1=1
     * @param array $filter 需要忽略更新的字段
     * @param array $keep 需要保留的更新字段
     * @return string 返回 update ... where ...语句
     * @since 1.0 2014-7-3 by sutroon created; 1.1 2014-10-17 by sutroon 新增$filter,$keep参数
     */
    public function get_update_sql($table, $search, $filter = null, $keep = null) {
        $data = $this->getParams($filter, $keep);
        if (!$data || !$search) {
            return ''; // 如果要全表更新需要加入 1=1
        }
        $str = '';
        foreach ($data as $k => $v) {
            $str .= "`$k`='$v',";
        }
        return sprintf('update %s set %s where %s;', $table, rtrim($str, ','), $search);
    }

    /**
     * 从添加的参数中获取存储过程调用命令
     * @param string $proc 过程或函数名
     * @param string 类型(PROC|FUNC)
     * @return string
     * @since 1.0 2014-6-28 by sutroon created; 1.1 2014-10-7 by sutroon 新增$type对函数的支持
     */
    public function get_procedure_sql($proc, $type = 'PROC') {
        if (!$this->params)
            return '';
        $strvalues = '';
        foreach ($this->params as $k => $v) {
            $strvalues .= "'$v',";
        }
        if ($strvalues) {
            $strvalues = rtrim($strvalues, ',');
        }
        if ($this->outParams)
            $strvalues .= ',' . $this->outParams;
        $strvalues = ltrim($strvalues, ',');
        return $type == 'PROC' ? sprintf('call %s (%s);', $proc, $strvalues) : sprintf('select %s(%s);', $proc, $strvalues);
    }

    /**
     * 执行存储过程
     * 如果有out参数则返回out参数结果集
     * @param string $proc 存储过程名称
     * @return mixed 返回array或boolean
     * @since 1.0 2014-6-30 by sutroon created; 1.1 2014-10-7 by sutroon 新增$type对函数的支持
     * @example
     *  1.带out存储过程 call sp_add (1,2,@a);select @a;
     */
    public function query_procedure($proc, $type = 'PROC') {
        if (!in_array('mysql', get_loaded_extensions())) {
            die('系统不支持mysql数据库扩展，请联系管理员开启!');
        }
        $conn = mysql_connect(C('DB_HOST'), C('DB_USER'), C('DB_PWD')) or die(mysql_error());
        mysql_select_db(C('DB_NAME'), $conn) or die(mysql_error());
        mysql_query('set names utf8');
        $sql = $this->get_procedure_sql($proc, $type);
        $result = mysql_query($sql) or die(mysql_error());
        if ($type == 'FUNC') {
            $arr_result = mysql_fetch_array($result);
            return $arr_result[0];
        }
        if (!$this->outParams) {
            return $result;
        }
        $result = mysql_query('select ' . $this->outParams) or die(mysql_error());
        return mysql_fetch_array($result);
    }

}
